
'''
package org.apache.jena.datatypes.xsd

import java.math.BigDecimal
import java.math.BigInteger
import java.net.URI

import org.apache.jena.datatypes.BaseDatatype
import org.apache.jena.datatypes.DatatypeFormatException
import org.apache.jena.datatypes.RDFDatatype
import org.apache.jena.datatypes.TypeMapper
import org.apache.jena.datatypes.xsd.impl.*
import org.apache.jena.ext.xerces.impl.dv.*
import org.apache.jena.ext.xerces.impl.dv.util.Base64
import org.apache.jena.ext.xerces.impl.dv.util.HexBin
import org.apache.jena.ext.xerces.impl.dv.xs.DecimalDV
import org.apache.jena.ext.xerces.impl.dv.xs.XSSimpleTypeDecl
import org.apache.jena.ext.xerces.impl.validation.ValidationState
import org.apache.jena.ext.xerces.util.SymbolHash
import org.apache.jena.ext.xerces.xs.XSConstants
import org.apache.jena.ext.xerces.xs.XSTypeDefinition
import org.apache.jena.graph.impl.LiteralLabel
'''


'''
 * Representation of an XSD datatype based on the Xerces-2
 * XSD implementation.
'''
import base64

from base.datatypes.BaseDatatype import BaseDatatype
from base.ext.xerces.xs import XSConstants


def newXSDDatatype(typename):
    return XSDDatatype(typename)


class XSDDatatype(BaseDatatype):

    def __init__(self, typename):
        super().__init__(typename)
        self.typename = typename


#=======================================================================
# local variables

    '''
     * the Xerces internal type declaration
    '''
    # XSSimpleType 
    typeDeclaration = None

    '''
     * the corresponding java primitive class, if any
    '''
    # param
    # return
    # protected
    # Class<?> javaClass = null

    '''
     * Used to access the values and facets of any of the decimal numeric types
    '''
    # static final DecimalDV decimalDV = DecimalDV()

#=======================================================================
# Methods

    '''
     * Constructor.
     *
     * @param typeName the name of the XSD type to be instantiated, this is
     *                 used to lookup a type definition from the Xerces schema factory.
    '''
    # param
    # return
    # XSDDatatype(String typeName):
        #super("")
        #typeDeclaration = SchemaDVFactory.getInstance().getBuiltInType(typeName)
        #uri = typeDeclaration.getNamespace() + "#" + typeDeclaration.getName()
    



    '''
     * Constructor.
     *
     * @param typeName  the name of the XSD type to be instantiated, this is
     *                  used to lookup a type definition from the Xerces schema factory.
     * @param javaClass the java class for which this xsd type is to be
     *                  treated as the canonical representation
    '''
    # param
    # return
    #XSDDatatype(String typeName, Class<?> javaClass):
        #this(typeName)
        #this.javaClass = javaClass
    

    '''
     * Constructor used when loading in external user defined XSD types via
     *
     * @param xstype    the XSSimpleType definition to be wrapped
     * @param namespace the namespace for the type (used because the grammar loading doesn't seem to keep that)
    '''
    # param
    # return
    # protected
    #XSDDatatype(XSSimpleType xstype, String namespace):
        #super("")
        #typeDeclaration = xstype
        #this.uri = namespace + "#" + typeDeclaration.getName()
    

    '''
     * Parse a lexical form of this datatype to a value
     *
     * @throws DatatypeFormatException if the lexical form is not legal
    '''
    # @Override
    # param String
    # return Object
    # throws DatatypeFormatException
    def parse(self, lexicalForm):
        '''
        try:
            #ValidationContext context = ValidationState()
            #ValidatedInfo resultInfo = ValidatedInfo()
            #typeDeclaration.validate(lexicalForm, context, resultInfo)
            return self.convertValidatedDataValue(resultInfo)
        except InvalidDatatypeValueException as e:
            raise DatatypeFormatException(lexicalForm, this, "during parse -" + e)
        '''
        pass
        
    

    '''
     * Convert a value of this datatype out
     * to lexical form.
    '''
    # @Override
    # param Object
    # return String
    def unparse(self, value):
        return value.toString()
    

    '''
     * Compares two instances of values of the given datatype.
    '''
    # @Override
    # param LiteralLabel LiteralLabel
    # return boolean
    def isEqual(self, value1, value2):
        return self.typeDeclaration.isEqual(value1.getValue(), value2.getValue())
    

    '''
     * If this datatype is used as the cannonical representation
     * for a particular java datatype then return that java type,
     * otherwise returns null.
    '''
    # @Override
    # class<?> getJavaClass():
    #     return javaClass
    

    '''
     * Returns the Xerces datatype representation for this type, this
     * is an XSSimpleType, in fact an XSSimpleTypeDecl.
    '''
    # @Override
    # param
    # return Object
    def extendedTypeDefinition(self):
        return self.typeDeclaration
    

    # The following code parses an external XSD file for XSD datatype definitions.
    # See an example "testing/xsd/daml+oil-ex-dt.xsd" and TestTypeLiterals.testUserDefined.
    # It calls into Xerces internal XM parsing, which is problematic if switching to the
    # JDK built-in XML parser in Java9 and later.

#    '''
#     * Create and register a set of types specified in a user schema file.
#     * We use the (illegal) DAML+OIL approach that the uriref of the type
#     * is the url of the schema file with fragment ID corresponding the
#     * the name of the type.
#     *
#     * @param uri the absolute uri of the schema file to be loaded
#     * @param reader the Reader stream onto the file (useful if you wish to load a cached copy of the schema file)
#     * @param encoding the encoding of the source file (can be null)
#     * @param tm the type mapper into which to load the definitions
#     * @return a List of strings giving the uri's of the newly defined datatypes
#     * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
#     * in default mode for load which may provide diagnostic output direct to stderr)
#    '''
#    # param
    # return
     #static List<String> loadUserDefined(String uri, Reader reader, String encoding, TypeMapper tm) throws DatatypeFormatException:
#        return loadUserDefined(XMLInputSource(null, uri, uri, reader, encoding), tm)
#    
#
#    '''
#     * Create and register a set of types specified in a user schema file.
#     * We use the (illegal) DAML+OIL approach that the uriref of the type
#     * is the url of the schema file with fragment ID corresponding the
#     * the name of the type.
#     *
#     * @param uri the absolute uri of the schema file to be loaded, this should be a resolvable URL
#     * @param encoding the encoding of the source file (can be null)
#     * @param tm the type mapper into which to load the definitions
#     * @return a List of strings giving the uri's of the newly defined datatypes
#     * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
#     * in default mode for load which may provide diagnostic output direct to stderr)
#    '''
#    # param
    # return
     # static List<String> loadUserDefined(String uri, String encoding, TypeMapper tm) throws DatatypeFormatException:
#        return loadUserDefined(XMLInputSource(null, uri, uri), tm)
#    
#
#    '''
#     * Internal implementation of loadUserDefined
#     *
#     * @param uri the absolute uri of the schema file to be loaded
#     * @param reader the Reader stream onto the file (useful if you wish to load a cached copy of the schema file)
#     * @param encoding the encoding of the source file (can be null)
#     * @param tm the type mapper into which to load the definitions
#     * @return a List of strings giving the uri's of the newly defined datatypes
#     * @throws DatatypeFormatException if there is a problem during load (not that we use Xerces
#     * in default mode for load which may provide diagnostic output direct to stderr)
#    '''
#    # param
    # return
    # private
     # static List<String> loadUserDefined(XMLInputSource source, TypeMapper tm) throws DatatypeFormatException:
#        XMLGrammarPreparser parser = XMLGrammarPreparser()
#        parser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null)
#        try:
#            XSGrammar xsg = (XSGrammar) parser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, source)
#            org.apache.xerces.xs.XSModel xsm = xsg.toXSModel()
#            XSNamedMap map = xsm.getComponents(XSTypeDefinition.SIMPLE_TYPE)
#            int numDefs = map.getLength()
#            ArrayList<String> names = ArrayList<>(numDefs)
#            for (int i = 0 i < numDefs i++):
#                XSSimpleType xstype = (XSSimpleType) map.item(i)
#                # Filter built in types - only needed for 2.6.0
#                if ( ! XSD.equals(xstype.getNamespace()) ):
#                    #xstype.derivedFrom()
#                    XSDDatatype definedType = XSDGenericType(xstype, source.getSystemId())
#                    tm.registerDatatype(definedType)
#                    names.add(definedType.getURI())
#                
#            
#            return names
#         catch (Exception e):
#            e.printStackTrace()    # Temp
#            raise DatatypeFormatException(e.toString())
#        
#    

    '''
     * Convert a validated xerces data value into the corresponding java data value.
     * This function is currently the most blatently xerces-version dependent part
     * of this subsystem. In many cases it also involves reparsing data which has
     * already been parsed as part of the validation.
     *
     * @param validatedInfo a fully populated Xerces data validation context
     * @return the appropriate java wrapper type
    '''
    # param ValidatedInfo
    # return Object
    # throws DatatypeFormatException
    def convertValidatedDataValue(self, validatedInfo):
        avt = validatedInfo.actualValueType
        if avt == XSConstants.BASE64BINARY_DT:
            # byte[]
            #decoded = Base64.decode(validatedInfo.normalizedValue)
            decoded = base64.decode(validatedInfo.normalizedValue)
            return (decoded)
        elif avt == XSConstants.BOOLEAN_DT:
            return validatedInfo.actualValue
        elif avt == XSConstants.HEXBINARY_DT:
            #decoded = HexBin.decode(validatedInfo.normalizedValue)
            #return (decoded)
            return validatedInfo.normalizedValue
        elif avt == XSConstants.UNSIGNEDSHORT_DT or avt == XSConstants.INT_DT:
            return self.trimPlus(validatedInfo.normalizedValue)
        elif avt == XSConstants.UNSIGNEDINT_DT or avt == XSConstants.LONG_DT:
            return self.suitableInteger(self.trimPlus(validatedInfo.normalizedValue))
        elif avt == XSConstants.UNSIGNEDBYTE_DT or avt == XSConstants.SHORT_DT \
            or avt == XSConstants.BYTE_DT:
            return self.trimPlus(validatedInfo.normalizedValue)
        elif avt == XSConstants.UNSIGNEDLONG_DT \
        or avt == XSConstants.INTEGER_DT \
        or avt == XSConstants.NONNEGATIVEINTEGER_DT \
        or avt == XSConstants.NONPOSITIVEINTEGER_DT \
        or avt == XSConstants.POSITIVEINTEGER_DT \
        or avt == XSConstants.NEGATIVEINTEGER_DT \
        or avt == XSConstants.DECIMAL_DT:
            # Object
            # xsdValue = validatedInfo.actualValue
            # if (decimalDV.getTotalDigits(xsdValue) == 0):
            #     return Integer.valueOf(0)
            #
            # if (decimalDV.getFractionDigits(xsdValue) >= 1):
            #     BigDecimal value = BigDecimal(self.trimPlus(validatedInfo.normalizedValue))
            #     return XSDdecimal.cannonicalise(value)
            #
            # # Can have 0 fractionDigits but still have a trailing .000
            # String lexical = self.trimPlus(validatedInfo.normalizedValue)
            # int dotx = lexical.indexOf('.')
            # if (dotx != -1):
            #     lexical = lexical.substring(0, dotx)
            #
            # if (decimalDV.getTotalDigits(xsdValue) > 18):
            #     return BigInteger(lexical)
            # else:
            #     return suitableInteger(lexical)
            pass
        else:
            return self.parseValidated(validatedInfo.normalizedValue)
        
    

    '''
     * @param lexical
     * @return Number
    '''
    # param String
    # return Number
    # protected
    def suitableInteger(self, lexical):
        # long 
        #number = Long.parseLong(lexical)
        #return suitableInteger(number)
        return lexical
    

    '''
     * @param number
     * @return Number
    '''
    # param
    # return
    # protected
    #static Number suitableInteger(long number):
        #if (number > Integer.MAX_VALUE || number < Integer.MIN_VALUE)
            #return Long.valueOf(number)
        #else
            #return Integer.valueOf((int) number)
    

    '''
     * Parse a validated lexical form. Subclasses which use the default
     * parse implementation and are not convered by the explicit convertValidatedData
     * cases should override this.
    '''
    # param String
    # return Object
    def parseValidated(self, lexical):
        return lexical
    

    '''
     * Test whether the given LiteralLabel is a valid instance
     * of this datatype. This takes into account typing information
     * as well as lexical form - for example an xsd:string is
     * never considered valid as an xsd:integer (even if it is
     * lexically legal like "1").
    '''
    # @Override
    # param LiteralLabel
    # return boolean 
    def isValidLiteral(self, lit):
        return self.isBaseTypeCompatible(lit) and self.isValid(lit.getLexicalForm())
    

    '''
     * Test if the given typed value is in the right partition of the XSD type space.
     * If this test passes then if the typed value has a legal lexical form for
     * this type then it is a legal instance.
    '''
    # param LiteralLabel
    # return boolean
    def isBaseTypeCompatible(self, lit):
        # XSTypeDefinition
        base = self.getFoundingType()
        # RDFDatatype
        litDT = lit.getDatatype()
        if isinstance(litDT, XSDDatatype):
            # XSTypeDefinition
            # litBase = ((XSDDatatype) litDT).getFoundingType()
            litBase = litDT.getFoundingType()
            return base.equals(litBase)

        elif (litDT is None and lit.language().equals("")):
            # Special RDF case, a plain literal is type compatible with and xsd:string-based type
            return base.equals(self.XSDstring.typeDeclaration)
        else:
            return False
        
    

    '''
     * Return the most specific type below xsd:anySimpleType that this type is derived from.
    '''
    # param
    # return XSTypeDefinition
    # private
    def getFoundingType(self):
        # XSTypeDefinition 
        founding = self.typeDeclaration
        # XSTypeDefinition 
        parent = founding.getBaseType()
        if (parent is None):
            # it is xsd:anySimpleType
            return founding
        while (parent.getBaseType() is not None):
            founding = parent
            parent = parent.getBaseType()
        
        return founding
    

    '''
     * Helper function to return the substring of a validated number string
     * omitting any leading + sign.
    '''
    # param String
    # return String
    @staticmethod
    def trimPlus(str):
        # int
        i = str.indexOf('+')
        if (i == -1):
            return str
        else:
            return str.substring(i + 1)
        
    

    '''
     * Add all of the XSD pre-defined simple types to the given
     * type mapper registry.
    '''
    # param
    # return void
    #
    '''
    @staticmethod
    void loadXSDSimpleTypes(TypeMapper tm):
        tm.registerDatatype(XSDDatatype("anySimpleType"))

        tm.registerDatatype(XSDdecimal)
        tm.registerDatatype(XSDinteger)
        tm.registerDatatype(XSDnonPositiveInteger)
        tm.registerDatatype(XSDnonNegativeInteger)
        tm.registerDatatype(XSDpositiveInteger)
        tm.registerDatatype(XSDnegativeInteger)

        tm.registerDatatype(XSDbyte)
        tm.registerDatatype(XSDunsignedByte)
        tm.registerDatatype(XSDdouble)
        tm.registerDatatype(XSDfloat)
        tm.registerDatatype(XSDlong)
        tm.registerDatatype(XSDunsignedInt)
        tm.registerDatatype(XSDunsignedShort)
        tm.registerDatatype(XSDunsignedLong)
        tm.registerDatatype(XSDint)
        tm.registerDatatype(XSDshort)

        tm.registerDatatype(XSDboolean)
        tm.registerDatatype(XSDbase64Binary)
        tm.registerDatatype(XSDhexBinary)

        tm.registerDatatype(XSDdate)
        tm.registerDatatype(XSDtime)
        tm.registerDatatype(XSDdateTime)
        tm.registerDatatype(XSDdateTimeStamp)
        tm.registerDatatype(XSDduration)
        tm.registerDatatype(XSDyearMonthDuration)
        tm.registerDatatype(XSDdayTimeDuration)
        tm.registerDatatype(XSDgYearMonth)
        tm.registerDatatype(XSDgMonthDay)
        tm.registerDatatype(XSDgMonth)
        tm.registerDatatype(XSDgDay)
        tm.registerDatatype(XSDgYear)

        tm.registerDatatype(XSDnormalizedString)
        tm.registerDatatype(XSDstring)
        tm.registerDatatype(XSDanyURI)

        tm.registerDatatype(XSDtoken)
        tm.registerDatatype(XSDName)
        tm.registerDatatype(XSDlanguage)
        tm.registerDatatype(XSDQName)
        tm.registerDatatype(XSDNMTOKEN)
        tm.registerDatatype(XSDID)
        tm.registerDatatype(XSDENTITY)
        tm.registerDatatype(XSDNCName)
        tm.registerDatatype(XSDNOTATION)
        tm.registerDatatype(XSDIDREF)

#        tm.registerDatatype(XSDIDREFS)
#        tm.registerDatatype(XSDENTITIES)
#        tm.registerDatatype(XSDNMTOKENS)
    '''

    '''
     * Generic XML Schema datatype (outside the xsd: namespace)
     * <p>
     * Datatype template that adapts any response back from Xerces type parsing
     * to an appropriate java representation. This is primarily used in creating
     * user defined types - the built in types have a fixed mapping.
    
    # param
    # return
     static class XSDGenericType extends XSDDatatype:

        
         * Hidden constructor used when loading in external user defined XSD
         * types
         *
         * @param xstype    the XSSimpleType definition to be wrapped
         * @param namespace the namespace for the type (used because the grammar
         *                  loading doesn't seem to keep that)
        
        XSDGenericType(XSSimpleType xstype, String namespace):
            super(xstype, namespace)
    '''
        
    

    # Used to bootstrap the above initialization code
    # param
    # return
    '''
    static void main(String[] args):
        SymbolHash types = SchemaDVFactory.getInstance().getBuiltInTypes()
        int len = types.getLength()
        Object[] values = Object[len]
        types.getValues(values, 0)
        for (Object value : values):
            if (value instanceof XSSimpleTypeDecl):
                XSSimpleTypeDecl decl = (XSSimpleTypeDecl) value
                System.out.println("tm.registerDatatype(XSDDatatype(\"" + decl.getName() + "\"))")
             else:
                System.out.println(" - " + value)
            
    '''
